package com.alibaba.citrus.ots.platform.enhance.facade.service.diff.sap;

import com.alibaba.acm.shaded.com.google.common.collect.Lists;
import com.alibaba.citrus.common.pair.Pair;
import com.alibaba.citrus.ots.common.annotation.FacadeInvoker;
import com.alibaba.citrus.ots.common.exception.FacadeException;
import com.alibaba.citrus.ots.common.util.SapAndCrmFieldDealUtils;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.DiffCheckRequest;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffResultStatus;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffTypeEnum;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.constant.DiffStrategyRouteDict;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.dataParser.DataParserManager;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffCheckProcessor;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffStrategyRoute;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.template.StandardTemplateDiff;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.sap.template.Sap017DetailStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.sap.template.Sap017SDOStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.repository.ConsignmentOrderRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.DiffErrorRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.ConsignmentOrder.ConsignmentOrderDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.ConsignmentOrder.ConsignmentOrderLineDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.*;
import com.alibaba.citrus.trail.log.LogEntry;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.nacos.common.utils.MapUtils;
import com.epoch.app.otsapplicationadaptersdk.sap.MnSapIf017Detail;
import com.epoch.app.otsapplicationadaptersdk.sap.MnSapIf017Request;
import com.epoch.app.otsapplicationadaptersdk.sap.MnSapIf017SDO;
import com.epoch.app.otsplatformenhance.model.dto.Sap017DiffRequest;
import com.epoch.app.otsplatformenhance.sap017diffcheck.dto.DiffCheckBySapDataRequest;
import com.epoch.app.otsplatformenhance.sap017diffcheck.service.Sap017DiffCheckService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author: qiaojiange
 * @create: 2022/01/23
 * @Description sap 017 对账
 */
@Service
@Primary
public class Sap017DiffCheckServiceImpl extends AbstractDiffCheck implements Sap017DiffCheckService {
    /**
     * 日志
     */
    private Log log = Log.getLogger(SapDiffCheckServiceImpl.class);
    /**
     * 标准对账模板
     */
    @Resource
    private StandardTemplateDiff standardTemplateDiff;

    /**
     * 数据解析器管理类
     */
    @Resource
    private DataParserManager dataParserManager;

    /**
     * 对账策略路由
     */
    @Resource
    private DiffStrategyRoute diffStrategyRoute;

    /**
     * 差错资源类
     */
    @Resource
    private DiffErrorRepository diffErrorRepository;

    @Resource
    private ConsignmentOrderRepository consignmentOrderRepository;

    @Override
    @FacadeInvoker
    public Result<String> diffCheck(Sap017DiffRequest sap017DiffRequest) {
        checkParam(sap017DiffRequest);
        super.doTraverse(sap017DiffRequest.getCondition(),sap017DiffRequest.getStartDate(),
                sap017DiffRequest.getEndDate());
        return Result.success("ture");
    }

    @Override
    void doHandlerLog(Pair<Integer, List<LogEntry>> pair) {
        List<DiffErrorDTO> diffErrorDTOList = new ArrayList<>();
        pair.right().forEach(logEntry -> {
            if (MapUtils.isNotEmpty(logEntry.contents())) {
                String sapMsg = logEntry.contents().get("input");
                log.info("sapMsg={}", sapMsg);
                JSONObject jsonObject = JSON.parseObject(sapMsg);
                String data = jsonObject.getString("body").trim();
                log.info("data={}", data);
                MnSapIf017Request sapIf017Request = JSON.parseObject(data, MnSapIf017Request.class);
                List<MnSapIf017SDO> sapIf017SDOList = sapIf017Request.getData();
                if (CollectionUtils.isNotEmpty(sapIf017SDOList)) {
                    for (MnSapIf017SDO sapIf017SDO : sapIf017SDOList) {
                        if (Objects.isNull(sapIf017SDO)) {
                            continue;
                        }

                        List<DiffErrorDTO> diffErrorDTOList1 = sap017Diff(sapIf017SDO);
                        if (CollectionUtils.isNotEmpty(diffErrorDTOList1)) {
                            diffErrorDTOList.addAll(diffErrorDTOList1);
                        }
                    }
                }
            }
        });

        if (CollectionUtils.isNotEmpty(diffErrorDTOList)) {
            diffErrorRepository.batchSaveDiffErrorList(diffErrorDTOList);
        }
    }


    /**
     * sap017 对账
     *
     * @param sapIf017SDO SAP017 数据
     * @return
     */
    private List<DiffErrorDTO> sap017Diff(MnSapIf017SDO sapIf017SDO) {
        List<DiffErrorDTO> diffErrorDTOList = new ArrayList<>();
        try {
            //1. 数据抽取
            Map<String, FieldLineDTO> sapIf017SDOMap = dataParserManager.parse(sapIf017SDO);

            //2. 和标准模板进行对比，检查是否缺的问题。 缺失有两个问题： a. 字段缺失， b. 数值缺失
            standardTemplateDiff.check(Sap017SDOStandardTemplate.obtainTemplateFieldSet(),
                    sapIf017SDOMap, Sap017SDOStandardTemplate.obtainIgnoreFieldSet());

            //3. 获取交货单
            Result<ConsignmentOrderDTO> consignmentOrderDTOResult = consignmentOrderRepository.queryBySap(sapIf017SDO);
            if (!consignmentOrderDTOResult.isSuccess()) {
                //埋点
                log.error("Sap017DiffCheckServiceImpl#sap017DiffCheck_fail");
                log.error("未查询到交货单");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf017SDO, sapIf017SDOMap,
                        null, "未查询到ots侧交货单信息");
                diffErrorDTOList.add(diffErrorDto);
                return diffErrorDTOList;
            }
            ConsignmentOrderDTO consignmentOrderDTO = consignmentOrderDTOResult.getResult();
            //4. 从交货单上抽取数据
            Map<String, FieldLineDTO> consignmentOrderDataMap = dataParserManager.parse(consignmentOrderDTO);
            //5. 和标准模板进行对比，检查是否缺的问题。
            standardTemplateDiff.check(Sap017SDOStandardTemplate.obtainTemplateFieldSet(), consignmentOrderDataMap,
                    Sap017SDOStandardTemplate.obtainIgnoreFieldSet());

            //6. 对账
            DiffCheckRequest diffCheckRequest = buildDiffCheckRequest(sapIf017SDOMap, consignmentOrderDataMap);
            DiffCheckProcessor diffCheckProcessor = diffStrategyRoute.obtainProcessor(diffCheckRequest);
            Result<String> diffResult = diffCheckProcessor.diff(diffCheckRequest);
            if (!diffResult.isSuccess()) {
                //埋点
                log.error("Sap017DiffCheckServiceImpl#sap017DiffCheck_fail");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf017SDO, sapIf017SDOMap,
                        consignmentOrderDataMap, diffResult.getMessage());
                diffErrorDTOList.add(diffErrorDto);
            }

            List<MnSapIf017Detail> sapIf017DetailList = sapIf017SDO.getItems();
            if (CollectionUtils.isNotEmpty(sapIf017DetailList)) {
                Map<String, ConsignmentOrderLineDTO> map = new HashMap<>();
                Result<List<ConsignmentOrderLineDTO>> consignmentOrderLineList = consignmentOrderRepository.queryConsignmentOrderLine("" + consignmentOrderDTO.getId());
                if (consignmentOrderLineList.isSuccess()
                        && CollectionUtils.isNotEmpty(consignmentOrderLineList.getResult())) {
                    map = consignmentOrderLineList.getResult().stream().collect(Collectors.toMap(ConsignmentOrderLineDTO::getOuterOrderLineId,
                            Function.identity()));
                }

                List<DiffErrorDTO> subDiffErrorDTOList = sap017DetailDiffCheck(sapIf017SDO.getDeliveryOrderId(), sapIf017DetailList, map);
                if (CollectionUtils.isNotEmpty(subDiffErrorDTOList)) {
                    diffErrorDTOList.addAll(subDiffErrorDTOList);
                }
            }
        } catch (Exception e) {
            //埋点
            log.error("Sap017DiffCheckServiceImpl#sap017DiffCheck_fail");
            log.error("sap017主单对账出错了", e);
            DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf017SDO, null,
                    null, "对账出异常了！");
            diffErrorDTOList.add(diffErrorDto);
        }
        return diffErrorDTOList;
    }

    /**
     * 构建差错记录对象
     *
     * @param sapIf017SDO             SAP017 主单
     * @param sapIf017SDOMap          SAP017 字段映射
     * @param consignmentOrderDataMap 交货单映射
     * @param result                  差错
     * @return 差错对象
     */
    private DiffErrorDTO buildDiffErrorDTO(MnSapIf017SDO sapIf017SDO, Map<String, FieldLineDTO> sapIf017SDOMap,
                                           Map<String, FieldLineDTO> consignmentOrderDataMap, String result) {

        Map<String, String> map = new HashMap<>();
        map.put("baseLineData", JSON.toJSONString(sapIf017SDO));

        //1. 封装差错记录
        DiffErrorDTO diffErrorDto = DiffErrorDTO.builder()
                .diffType(DiffTypeEnum.SAP017_DIFF_CONSIGNMENT_ORDER.getDiffType())
                //业务流程描述
                .diffDesc(DiffTypeEnum.SAP017_DIFF_CONSIGNMENT_ORDER.getDiffDesc())
                //对账结果描述
                .result(result)
                //存的是交货单 ID
                .mainOrderId(sapIf017SDO.getDeliveryOrderId())
                .diffData1(MapUtils.isNotEmpty(sapIf017SDOMap) ? JSON.toJSONString(sapIf017SDOMap) : null)
                .diffData2(MapUtils.isNotEmpty(sapIf017SDOMap) ? JSON.toJSONString(consignmentOrderDataMap) : null)
                .strategyRoute(DiffStrategyRouteDict.SAP017_DIFF_CONSIGNMENT_ORDER.getStrategyCode())
                .features(JSON.toJSONString(map))
                .status(DiffResultStatus.FAIL.getStatus()).build();
        return diffErrorDto;
    }

    /**
     * 构建对账请求
     *
     * @param sapIf017SDOMap          SAP017 主单映射
     * @param consignmentOrderDataMap 交货单字段映射
     * @return
     */
    private DiffCheckRequest buildDiffCheckRequest(Map<String, FieldLineDTO> sapIf017SDOMap, Map<String, FieldLineDTO> consignmentOrderDataMap) {
        DiffCheckRequest diffCheckRequest = new DiffCheckRequest();
        //对账类型
        diffCheckRequest.setDiffType(DiffTypeEnum.SAP017_DIFF_CONSIGNMENT_ORDER.getDiffType());
        //对账类型描述（就是业务流程描述）
        diffCheckRequest.setDiffDesc(DiffTypeEnum.SAP017_DIFF_CONSIGNMENT_ORDER.getDiffDesc());
        //策略路由
        diffCheckRequest.setStrategyRoute(DiffStrategyRouteDict.SAP017_DIFF_CONSIGNMENT_ORDER.getStrategyCode());
        //基准数据，key 为标准字段
        diffCheckRequest.setBaseLineDataMap(sapIf017SDOMap);
        //被对账数据
        diffCheckRequest.setDiffDataMap(consignmentOrderDataMap);

        return diffCheckRequest;
    }

    /**
     * SAP017子单对账
     *
     * @param consignmentOrderId
     * @param sapIf017DetailList SAP 子单列表
     * @param map
     * @return
     */
    private List<DiffErrorDTO> sap017DetailDiffCheck(String consignmentOrderId, List<MnSapIf017Detail> sapIf017DetailList, Map<String, ConsignmentOrderLineDTO> map) {
        List<DiffErrorDTO> diffErrorDTOList = Lists.newArrayList();
        for (MnSapIf017Detail sapIf017Detail : sapIf017DetailList) {
            try {
                Map<String, FieldLineDTO> sapIf017DetailDataMap = dataParserManager.parse(sapIf017Detail);
                standardTemplateDiff.check(Sap017DetailStandardTemplate.obtainTemplateFieldSet(), sapIf017DetailDataMap,
                        Sap017DetailStandardTemplate.obtainIgnoreFieldSet());
                //3. 获取交货子单
                ConsignmentOrderLineDTO consignmentOrderLineDTO = map.get(SapAndCrmFieldDealUtils.generateIdField(sapIf017Detail.getOrderLineId()));
                //4. 从交货子单上抽取数据
                Map<String, FieldLineDTO> consignmentOrderLineDataMap = dataParserManager.parse(consignmentOrderLineDTO);
                //5. 和标准模板进行对比，检查是否缺的问题。
                Result<String> checkResult = standardTemplateDiff.check(Sap017DetailStandardTemplate.obtainTemplateFieldSet(), consignmentOrderLineDataMap,
                        Sap017DetailStandardTemplate.obtainIgnoreFieldSet());
                if (!checkResult.isSuccess()) {
                    log.error("Sap017DiffCheckServiceImpl#sap017DetailDiffCheck_fail");
                    DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(consignmentOrderId, sapIf017Detail,
                            sapIf017DetailDataMap, consignmentOrderLineDataMap, checkResult.getMessage());
                    diffErrorDTOList.add(diffErrorDTO);
                    continue;
                }
                //6. 对账
                DiffCheckRequest detailDiffCheckRequest = buildDetailDiffCheckRequest(sapIf017DetailDataMap, consignmentOrderLineDataMap);
                DiffCheckProcessor diffCheckProcessor = diffStrategyRoute.obtainProcessor(detailDiffCheckRequest);

                Result<String> diffResult = diffCheckProcessor.diff(detailDiffCheckRequest);
                if (!diffResult.isSuccess()) {
                    //埋点
                    log.error("Sap017DiffCheckServiceImpl#sap017DetailDiffCheck_fail");
                    DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(consignmentOrderId, sapIf017Detail,
                            sapIf017DetailDataMap, consignmentOrderLineDataMap, diffResult.getMessage());
                    diffErrorDTOList.add(diffErrorDTO);
                }
            } catch (Exception e) {
                //埋点
                log.error("Sap017DiffCheckServiceImpl#sap017DetailDiffCheck_fail");
                log.error("sap017子单对账出错了", e);
                DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(consignmentOrderId, sapIf017Detail,
                        null, null, "对账出异常了！");
                diffErrorDTOList.add(diffErrorDTO);
            }
        }
        return diffErrorDTOList;
    }

    /**
     * 构建子单的差错记录
     */
    private DiffErrorDTO buildDetailDiffErrorDTO(String consignmentOrderId, MnSapIf017Detail sapIf017Detail, Map<String, FieldLineDTO> sapIf017DetailDataMap,
                                                 Map<String, FieldLineDTO> consignmentOrderLineDataMap, String result) {
        Map<String, String> map = new HashMap<>();
        map.put("baseLineData", JSON.toJSONString(sapIf017Detail));

        //1. 封装差错记录
        DiffErrorDTO diffErrorDto = DiffErrorDTO.builder()
                .diffType(DiffTypeEnum.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getDiffType())
                //业务流程描述
                .diffDesc(DiffTypeEnum.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getDiffDesc())
                //对账结果描述
                .result(result)
                //交货单 ID
                .mainOrderId(consignmentOrderId)
                //交货子单 ID
                .subOrderId(sapIf017Detail.getOrderLineId())
                //基准数据
                .diffData1(MapUtils.isNotEmpty(sapIf017DetailDataMap) ? JSON.toJSONString(sapIf017DetailDataMap) : null)
                .diffData2(MapUtils.isNotEmpty(consignmentOrderLineDataMap) ? JSON.toJSONString(consignmentOrderLineDataMap) : null)
                .strategyRoute(DiffStrategyRouteDict.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getStrategyCode())
                .features(JSON.toJSONString(map))
                .status(DiffResultStatus.FAIL.getStatus()).build();
        return diffErrorDto;
    }

    /**
     * 构建子单对账请求
     *
     * @param sapIf017DetailDataMap       子单请求
     * @param consignmentOrderLineDataMap 交货子单字段列表
     * @return 构建对账请求
     */
    private DiffCheckRequest buildDetailDiffCheckRequest(Map<String, FieldLineDTO> sapIf017DetailDataMap, Map<String, FieldLineDTO> consignmentOrderLineDataMap) {
        DiffCheckRequest diffCheckRequest = new DiffCheckRequest();
        //对账类型
        diffCheckRequest.setDiffType(DiffTypeEnum.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getDiffType());
        //对账类型描述（就是业务流程描述）
        diffCheckRequest.setDiffDesc(DiffTypeEnum.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getDiffDesc());
        //策略路由
        diffCheckRequest.setStrategyRoute(DiffStrategyRouteDict.SAP017_DETAIL_DIFF_CONSIGNMENT_ORDER_LINE.getStrategyCode());
        //基准数据，key 为标准字段
        diffCheckRequest.setBaseLineDataMap(MapUtils.isNotEmpty(sapIf017DetailDataMap) ? sapIf017DetailDataMap : null);
        //被对账数据
        diffCheckRequest.setDiffDataMap(MapUtils.isNotEmpty(consignmentOrderLineDataMap) ? consignmentOrderLineDataMap : null);
        return diffCheckRequest;
    }

    /**
     * 通过 SAP 报文进行对账, 该方法是为了提供给页面上进行手动对账使用
     *
     * @param diffCheckBySapDataRequest SAP 报文对账请求
     * @return 对账结果
     */
    @FacadeInvoker
    public Result<String> diffCheckBySapData(DiffCheckBySapDataRequest diffCheckBySapDataRequest) {
        String sap017Data = diffCheckBySapDataRequest.getSap017JsonData();
        MnSapIf017SDO sapIf017SDO = JSON.parseObject(sap017Data, MnSapIf017SDO.class);
        List<DiffErrorDTO> diffErrorDTOList = sap017Diff(sapIf017SDO);
        if (CollectionUtils.isEmpty(diffErrorDTOList)) {
            return Result.success("对账完毕，数据一致！");
        } else {
            return Result.fail("", JSON.toJSONString(diffErrorDTOList));
        }
    }

    /**
     * 校验参数
     *
     * @param sap017DiffRequest SAP 017请求
     */
    private void checkParam(Sap017DiffRequest sap017DiffRequest) {
        if (Objects.isNull(sap017DiffRequest)) {
            throw new FacadeException("OTS-13-001-00-16-003");
        }

        if (Objects.isNull(sap017DiffRequest.getStartDate())) {
            throw new FacadeException("OTS-13-001-00-16-004");
        }

        if (Objects.isNull(sap017DiffRequest.getEndDate())) {
            throw new FacadeException("OTS-13-001-00-16-005");
        }

        if (StringUtils.isBlank(sap017DiffRequest.getCondition())) {
            throw new FacadeException("OTS-13-001-00-16-011");
        }
    }

}
